Odemkněte sílu WebCodecs! Průvodce přístupem k datům video snímků pomocí rovin VideoFrame. Naučte se o formátech pixelů, rozložení paměti a pokročilém zpracování videa v prohlížeči.
WebCodecs VideoFrame Plane: Hloubkový pohled na přístup k datům video snímku
WebCodecs představují změnu paradigmatu ve zpracování médií na webu. Poskytují nízkoúrovňový přístup ke stavebním kamenům médií, což vývojářům umožňuje vytvářet sofistikované aplikace přímo v prohlížeči. Jednou z nejvýkonnějších funkcí WebCodecs je objekt VideoFrame a v něm roviny VideoFrame, které odhalují surová pixelová data video snímků. Tento článek poskytuje komplexního průvodce pro pochopení a využití rovin VideoFrame pro pokročilou manipulaci s videem.
Porozumění objektu VideoFrame
Než se ponoříme do rovin, zrekapitulujme si samotný objekt VideoFrame. VideoFrame představuje jeden snímek videa. Zapouzdřuje dekódovaná (nebo zakódovaná) video data spolu s přidruženými metadaty, jako jsou časové razítko, trvání a informace o formátu. API VideoFrame nabízí metody pro:
- Čtení pixelových dat: Zde přicházejí na řadu roviny.
- Kopírování snímků: Vytváření nových objektů
VideoFramez existujících. - Zavírání snímků: Uvolnění podkladových zdrojů držených snímkem.
Objekt VideoFrame se vytváří během procesu dekódování, typicky pomocí VideoDecoder, nebo ručně při vytváření vlastního snímku.
Co jsou roviny VideoFrame?
Pixelová data objektu VideoFrame jsou často uspořádána do více rovin, zejména ve formátech jako YUV. Každá rovina představuje jinou složku obrazu. Například ve formátu YUV420 existují tři roviny:
- Y (Luma): Představuje jas (luminanci) obrazu. Tato rovina obsahuje informace o odstínech šedi.
- U (Cb): Představuje modrou-rozdílovou barevnou složku (chrominanci).
- V (Cr): Představuje červenou-rozdílovou barevnou složku (chrominanci).
Formáty RGB, ač se zdají jednodušší, mohou v některých případech také používat více rovin. Počet rovin a jejich význam závisí výhradně na VideoPixelFormat objektu VideoFrame.
Výhodou použití rovin je, že umožňuje efektivní přístup a manipulaci se specifickými barevnými složkami. Můžete například chtít upravit pouze jas (rovina Y), aniž byste ovlivnili barvu (roviny U a V).
Přístup k rovinám VideoFrame: API
API VideoFrame poskytuje následující metody pro přístup k datům rovin:
copyTo(destination, options): Zkopíruje obsahVideoFramedo cíle, kterým může být jinýVideoFrame,CanvasImageBitmapneboArrayBufferView. Objektoptionsřídí, které roviny se kopírují a jak. Toto je primární mechanismus pro přístup k rovinám.
Objekt options v metodě copyTo umožňuje specifikovat rozložení a cíl pro data video snímku. Klíčové vlastnosti zahrnují:
format: Požadovaný formát pixelů kopírovaných dat. Může být stejný jako původníVideoFramenebo jiný formát (např. převod z YUV na RGB).codedWidthacodedHeight: Šířka a výška video snímku v pixelech.layout: Pole objektů popisujících rozložení každé roviny v paměti. Každý objekt v poli specifikuje:offset: Posun v bajtech od začátku datového bufferu k začátku dat roviny.stride: Počet bajtů mezi začátkem každého řádku v rovině. To je klíčové pro zpracování odsazení (padding).
Podívejme se na příklad kopírování VideoFrame ve formátu YUV420 do surového bufferu:
async function copyYUV420ToBuffer(videoFrame, buffer) {
const width = videoFrame.codedWidth;
const height = videoFrame.codedHeight;
// YUV420 má 3 roviny: Y, U a V
const yPlaneSize = width * height;
const uvPlaneSize = width * height / 4;
const layout = [
{ offset: 0, stride: width }, // Rovina Y
{ offset: yPlaneSize, stride: width / 2 }, // Rovina U
{ offset: yPlaneSize + uvPlaneSize, stride: width / 2 } // Rovina V
];
await videoFrame.copyTo(buffer, {
format: 'I420',
codedWidth: width,
codedHeight: height,
layout: layout
});
videoFrame.close(); // Důležité pro uvolnění zdrojů
}
Vysvětlení:
- Vypočítáme velikost každé roviny na základě
widthaheight. Y má plné rozlišení, zatímco U a V jsou podvzorkovány (4:2:0). - Pole
layoutdefinuje rozložení v paměti.offsetspecifikuje, kde každá rovina začíná v bufferu, astridespecifikuje počet bajtů, o které je třeba skočit, abyste se dostali na další řádek v dané rovině. - Volba
formatje nastavena na 'I420', což je běžný formát YUV420. - Je kriticky důležité, aby po kopírování byla zavolána metoda
videoFrame.close()pro uvolnění zdrojů.
Formáty pixelů: Svět možností
Porozumění formátům pixelů je klíčové pro práci s rovinami VideoFrame. VideoPixelFormat definuje, jak jsou barevné informace zakódovány ve video snímku. Zde jsou některé běžné formáty pixelů, se kterými se můžete setkat:
- I420 (YUV420p): Planární formát YUV, kde jsou složky Y, U a V uloženy v samostatných rovinách. U a V jsou podvzorkovány faktorem 2 v horizontálním i vertikálním směru. Je to velmi běžný a efektivní formát.
- NV12 (YUV420sp): Semi-planární formát YUV, kde je Y uloženo v jedné rovině a složky U a V jsou prokládané v druhé rovině.
- RGBA: Složky červená, zelená, modrá a alfa jsou uloženy v jedné rovině, obvykle s 8 bity na složku (32 bitů na pixel). Pořadí složek se může lišit (např. BGRA).
- RGB565: Složky červená, zelená a modrá jsou uloženy v jedné rovině s 5 bity pro červenou, 6 bity pro zelenou a 5 bity pro modrou (16 bitů na pixel).
- GRAYSCALE: Reprezentuje obrázky v odstínech šedi s jedinou hodnotou luma (jasu) pro každý pixel.
Vlastnost VideoFrame.format vám řekne, jaký formát pixelů má daný snímek. Nezapomeňte tuto vlastnost zkontrolovat před pokusem o přístup k rovinám. Úplný seznam podporovaných formátů naleznete ve specifikaci WebCodecs.
Praktické případy použití
Přístup k rovinám VideoFrame otevírá širokou škálu možností pro pokročilé zpracování videa v prohlížeči. Zde jsou některé příklady:
1. Video efekty v reálném čase
Můžete aplikovat video efekty v reálném čase manipulací s pixelovými daty v VideoFrame. Mohli byste například implementovat filtr pro odstíny šedi zprůměrováním složek R, G a B každého pixelu v RGBA snímku a následným nastavením všech tří složek na tuto průměrnou hodnotu. Můžete také vytvořit sépiový efekt nebo upravit jas a kontrast.
async function applyGrayscale(videoFrame) {
const width = videoFrame.codedWidth;
const height = videoFrame.codedHeight;
const buffer = new ArrayBuffer(width * height * 4); // RGBA
const rgba = new Uint8ClampedArray(buffer);
await videoFrame.copyTo(rgba, {
format: 'RGBA',
codedWidth: width,
codedHeight: height
});
for (let i = 0; i < rgba.length; i += 4) {
const r = rgba[i];
const g = rgba[i + 1];
const b = rgba[i + 2];
const gray = (r + g + b) / 3;
rgba[i] = gray; // Červená
rgba[i + 1] = gray; // Zelená
rgba[i + 2] = gray; // Modrá
}
// Vytvoření nového VideoFrame z upravených dat.
const newFrame = new VideoFrame(rgba, {
format: 'RGBA',
codedWidth: width,
codedHeight: height,
timestamp: videoFrame.timestamp,
duration: videoFrame.duration
});
videoFrame.close(); // Uvolnění původního snímku
return newFrame;
}
2. Aplikace počítačového vidění
Roviny VideoFrame poskytují přímý přístup k pixelovým datům potřebným pro úlohy počítačového vidění. Tato data můžete použít k implementaci algoritmů pro detekci objektů, rozpoznávání obličejů, sledování pohybu a další. Pro výkonově kritické části kódu můžete využít WebAssembly.
Například byste mohli převést barevný VideoFrame na odstíny šedi a poté aplikovat algoritmus pro detekci hran (např. Sobelův operátor) k identifikaci hran v obraze. To by mohlo být použito jako předzpracovací krok pro rozpoznávání objektů.
3. Střih a kompozice videa
Roviny VideoFrame můžete použít k implementaci funkcí pro střih videa, jako je ořezávání, změna velikosti, rotace a kompozice. Přímou manipulací s pixelovými daty můžete vytvářet vlastní přechody a efekty.
Například byste mohli oříznout VideoFrame zkopírováním pouze části pixelových dat do nového VideoFrame. Museli byste odpovídajícím způsobem upravit posuny a kroky (strides) v layout.
4. Vlastní kodeky a překódování
Ačkoli WebCodecs poskytuje vestavěnou podporu pro běžné kodeky jako AV1, VP9 a H.264, můžete jej také použít k implementaci vlastních kodeků nebo překódovacích pipeline. Museli byste si sami zpracovat proces kódování a dekódování, ale roviny VideoFrame vám umožňují přístup a manipulaci se surovými pixelovými daty. To by mohlo být užitečné pro specializované video formáty nebo specifické požadavky na kódování.
5. Pokročilá analytika
Přístupem k podkladovým pixelovým datům můžete provádět hloubkovou analýzu video obsahu. To zahrnuje úkoly jako měření průměrného jasu scény, identifikaci dominantních barev nebo detekci změn v obsahu scény. To může umožnit pokročilé analytické aplikace pro bezpečnost, dohled nebo analýzu obsahu.
Práce s Canvas a WebGL
I když můžete přímo manipulovat s pixelovými daty v rovinách VideoFrame, často potřebujete výsledek vykreslit na obrazovku. Rozhraní CanvasImageBitmap poskytuje most mezi VideoFrame a elementem <canvas>. Můžete vytvořit CanvasImageBitmap z VideoFrame a poté jej vykreslit na canvas pomocí metody drawImage().
async function renderVideoFrameToCanvas(videoFrame, canvas) {
const bitmap = await createImageBitmap(videoFrame);
const ctx = canvas.getContext('2d');
ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
bitmap.close(); // Uvolnění zdrojů bitmapy
videoFrame.close(); // Uvolnění zdrojů VideoFrame
}
Pro pokročilejší vykreslování můžete použít WebGL. Můžete nahrát pixelová data z rovin VideoFrame do WebGL textur a poté použít shadery k aplikaci efektů a transformací. To vám umožní využít GPU pro vysoce výkonné zpracování videa.
Otázky výkonu
Práce se surovými pixelovými daty může být výpočetně náročná, proto je klíčové zvážit optimalizaci výkonu. Zde je několik tipů:
- Minimalizujte kopírování: Vyhněte se zbytečnému kopírování pixelových dat. Pokuste se provádět operace na místě, kdykoli je to možné.
- Použijte WebAssembly: Pro výkonově kritické části kódu zvažte použití WebAssembly. WebAssembly může poskytnout téměř nativní výkon pro výpočetně náročné úkoly.
- Optimalizujte rozložení paměti: Zvolte správný formát pixelů a rozložení paměti pro vaši aplikaci. Zvažte použití zhuštěných formátů (např. RGBA), pokud nepotřebujete často přistupovat k jednotlivým barevným složkám.
- Použijte OffscreenCanvas: Pro zpracování na pozadí použijte
OffscreenCanvas, abyste neblokovali hlavní vlákno. - Profilujte svůj kód: Použijte vývojářské nástroje prohlížeče k profilování kódu a identifikaci úzkých míst výkonu.
Kompatibilita s prohlížeči
WebCodecs a API VideoFrame jsou podporovány ve většině moderních prohlížečů, včetně Chrome, Firefoxu a Safari. Úroveň podpory se však může lišit v závislosti na verzi prohlížeče a operačním systému. Zkontrolujte nejnovější tabulky kompatibility prohlížečů na stránkách jako MDN Web Docs, abyste se ujistili, že funkce, které používáte, jsou podporovány ve vašich cílových prohlížečích. Pro zajištění kompatibility napříč prohlížeči se doporučuje detekce funkcí.
Běžné nástrahy a řešení problémů
Zde jsou některé běžné nástrahy, kterým je třeba se vyhnout při práci s rovinami VideoFrame:
- Nesprávné rozložení: Ujistěte se, že pole
layoutpřesně popisuje rozložení pixelových dat v paměti. Nesprávné posuny nebo kroky (strides) mohou vést k poškozeným obrázkům. - Neshodující se formáty pixelů: Ujistěte se, že formát pixelů, který specifikujete v metodě
copyTo, odpovídá skutečnému formátuVideoFrame. - Úniky paměti: Vždy zavírejte objekty
VideoFrameaCanvasImageBitmappoté, co s nimi skončíte, abyste uvolnili podkladové zdroje. Pokud tak neučiníte, může to vést k únikům paměti. - Asynchronní operace: Pamatujte, že
copyToje asynchronní operace. Použijteawait, abyste zajistili, že operace kopírování bude dokončena, než budete přistupovat k pixelovým datům. - Bezpečnostní omezení: Buďte si vědomi bezpečnostních omezení, která se mohou vztahovat na přístup k pixelovým datům z videí z cizího zdroje (cross-origin).
Příklad: Konverze YUV na RGB
Podívejme se na složitější příklad: převod VideoFrame z formátu YUV420 na RGB VideoFrame. To zahrnuje čtení rovin Y, U a V, jejich převod na hodnoty RGB a následné vytvoření nového RGB VideoFrame.
Tuto konverzi lze implementovat pomocí následujícího vzorce:
R = Y + 1.402 * (Cr - 128)
G = Y - 0.34414 * (Cb - 128) - 0.71414 * (Cr - 128)
B = Y + 1.772 * (Cb - 128)
Zde je kód:
async function convertYUV420ToRGBA(videoFrame) {
const width = videoFrame.codedWidth;
const height = videoFrame.codedHeight;
const yPlaneSize = width * height;
const uvPlaneSize = width * height / 4;
const yuvBuffer = new ArrayBuffer(yPlaneSize + 2 * uvPlaneSize);
const yuvPlanes = new Uint8ClampedArray(yuvBuffer);
const layout = [
{ offset: 0, stride: width }, // Rovina Y
{ offset: yPlaneSize, stride: width / 2 }, // Rovina U
{ offset: yPlaneSize + uvPlaneSize, stride: width / 2 } // Rovina V
];
await videoFrame.copyTo(yuvPlanes, {
format: 'I420',
codedWidth: width,
codedHeight: height,
layout: layout
});
const rgbaBuffer = new ArrayBuffer(width * height * 4);
const rgba = new Uint8ClampedArray(rgbaBuffer);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const yIndex = y * width + x;
const uIndex = Math.floor(y / 2) * (width / 2) + Math.floor(x / 2) + yPlaneSize;
const vIndex = Math.floor(y / 2) * (width / 2) + Math.floor(x / 2) + yPlaneSize + uvPlaneSize;
const Y = yuvPlanes[yIndex];
const U = yuvPlanes[uIndex] - 128;
const V = yuvPlanes[vIndex] - 128;
let R = Y + 1.402 * V;
let G = Y - 0.34414 * U - 0.71414 * V;
let B = Y + 1.772 * U;
R = Math.max(0, Math.min(255, R));
G = Math.max(0, Math.min(255, G));
B = Math.max(0, Math.min(255, B));
const rgbaIndex = y * width * 4 + x * 4;
rgba[rgbaIndex] = R;
rgba[rgbaIndex + 1] = G;
rgba[rgbaIndex + 2] = B;
rgba[rgbaIndex + 3] = 255; // Alfa
}
}
const newFrame = new VideoFrame(rgba, {
format: 'RGBA',
codedWidth: width,
codedHeight: height,
timestamp: videoFrame.timestamp,
duration: videoFrame.duration
});
videoFrame.close(); // Uvolnění původního snímku
return newFrame;
}
Tento příklad demonstruje sílu a složitost práce s rovinami VideoFrame. Vyžaduje dobré porozumění formátům pixelů, rozložení paměti a konverzím barevných prostorů.
Závěr
API rovin VideoFrame v WebCodecs odemyká novou úroveň kontroly nad zpracováním videa v prohlížeči. Pochopením toho, jak přistupovat a manipulovat s pixelovými daty přímo, můžete vytvářet pokročilé aplikace pro video efekty v reálném čase, počítačové vidění, střih videa a další. Ačkoli práce s rovinami VideoFrame může být náročná, potenciální odměny jsou značné. Jak se WebCodecs neustále vyvíjí, bezpochyby se stane nezbytným nástrojem pro webové vývojáře pracující s médii.
Doporučujeme vám experimentovat s API rovin VideoFrame a prozkoumat jeho možnosti. Pochopením základních principů a uplatňováním osvědčených postupů můžete vytvářet inovativní a výkonné video aplikace, které posouvají hranice toho, co je v prohlížeči možné.
Další zdroje
- MDN Web Docs o WebCodecs
- Specifikace WebCodecs
- Repozitáře se vzorovým kódem WebCodecs na GitHubu.